/*
 * Copyright 2005-2016 sxhuayuan.com. All rights reserved.
 */
package com.sxhuayuan.parking.compenent.shiro.filter;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sxhuayuan.parking.compenent.shiro.AccountType;
import com.sxhuayuan.parking.compenent.shiro.AuthenticationToken;
import com.sxhuayuan.parking.compenent.shiro.Principal;
import com.sxhuayuan.parking.compenent.web.RespMessage;
import com.sxhuayuan.parking.entity.Log;
import com.sxhuayuan.parking.service.LogService;
import com.sxhuayuan.parking.utils.SpringUtils;

/**
 * 当request为登录url时，会调用createToken来组装Token，然后自动执行login方法
 * 
 * @author gaoyan
 * @version 1.0
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {

	Logger log = LoggerFactory.getLogger(getClass());

	/** 默认"加密密码"参数名称 */
	private static final String DEFAULT_EN_PASSWORD_PARAM = "password";

	/** 默认"验证ID"参数名称 */
	private static final String DEFAULT_CAPTCHA_ID_PARAM = "captchaId";

	/** 默认"验证码"参数名称 */
	private static final String DEFAULT_CAPTCHA_PARAM = "captcha";

	/** "加密密码"参数名称 */
	private String enPasswordParam = DEFAULT_EN_PASSWORD_PARAM;

	/** "验证ID"参数名称 */
	private String captchaIdParam = DEFAULT_CAPTCHA_ID_PARAM;

	/** "验证码"参数名称 */
	private String captchaParam = DEFAULT_CAPTCHA_PARAM;

	private LogService logService;

	public void setLogService(LogService logService) {
		this.logService = logService;
	}

	@Override
	protected org.apache.shiro.authc.AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) {
		HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
		String uri = httpRequest.getRequestURI();
		log.debug("登录uri={}", uri);
		uri = uri.substring(1);// 删掉开头的'/'
		AccountType type = AccountType.valueOf(uri.substring(0, uri.indexOf("/")));
		String username = getUsername(servletRequest);
		String password = getPassword(servletRequest);
		String captchaId = getCaptchaId(servletRequest);
		String captcha = getCaptcha(servletRequest);
		String host = getHost(servletRequest);
		return new AuthenticationToken(type, username, password, captchaId, captcha, false, host);
	}

	// 请求失败的方法
	@Override
	protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		if (isLoginRequest(request, response)) {
			if (isLoginSubmission(request, response)) {
				if (log.isTraceEnabled()) {
					log.trace("Login submission detected.  Attempting to execute login.");
				}
				return executeLogin(request, response);
			} else {
				if (log.isTraceEnabled()) {
					log.trace("Login page view.");
				}
				// allow them to see the login page ;)
				return true;
			}
		} else {
			if (log.isTraceEnabled()) {
				log.trace("Attempting to access a path which requires authentication.  Forwarding to the " + "Authentication url [" + getLoginUrl() + "]");
			}
			response.setCharacterEncoding("utf-8");
			response.setContentType("application/json");
			response.getWriter().write(RespMessage.builder().invalidTokenError().build().toString());
			return false;
		}
	}

	/** 登录成功后的操作 */
	@Override
	protected boolean onLoginSuccess(org.apache.shiro.authc.AuthenticationToken token, Subject subject, ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
		/** 重新生成session */
		Session session = subject.getSession();
		Map<Object, Object> attributes = new HashMap<Object, Object>();
		Collection<Object> keys = session.getAttributeKeys();
		for (Object key : keys) {
			attributes.put(key, session.getAttribute(key));
		}
		session.stop();
		session = subject.getSession();
		for (Entry<Object, Object> entry : attributes.entrySet()) {
			session.setAttribute(entry.getKey(), entry.getValue());
		}
		// 日志
		Log log = new Log();
		AuthenticationToken authenticationToken = (AuthenticationToken) token;
		log.setOperation(SpringUtils.getMessage(authenticationToken.getAccountType() + ".login.login"));
		Principal principal = (Principal) subject.getPrincipal();
		log.setOperator(principal.getUsername());
		log.setIp(servletRequest.getRemoteAddr());
		logService.save(log);
		return super.onLoginSuccess(token, subject, servletRequest, servletResponse);
	}

	@Override
	protected void issueSuccessRedirect(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
		// HttpServletRequest request = (HttpServletRequest) servletRequest;
		// String requestURI =
		// WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
		// log.debug("requestURI = " + requestURI);
		// WebUtils.redirectToSavedRequest(request, response, requestURI);
		servletResponse.setContentType("application/json");
		servletResponse.setCharacterEncoding("utf-8");
		servletResponse.getWriter().write(RespMessage.builder().success().setDatas(SecurityUtils.getSubject().getSession().getId()).build().toString());
		// super.issueSuccessRedirect(request, response);
	}

	@Override
	protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
		// super.redirectToLogin(request, response);
		String requestURI = WebUtils.getPathWithinApplication(WebUtils.toHttp(request));
		log.debug("requestURI = " + requestURI);
		String loginUrl = "/" + requestURI.split("/")[1] + "/login";
		WebUtils.issueRedirect(request, response, loginUrl);
	}

	/** 从request中获取用户密码并用私钥解密 */
	@Override
	protected String getPassword(ServletRequest servletRequest) {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		String password = null;
		try {
			// password = RSAUtils.decrypt(SettingUtils.get().getPrivateKey(),
			// request.getParameter(enPasswordParam));
			password = request.getParameter(enPasswordParam);
		} catch (Exception e) {
			log.error("", e);
		}
		return password;
	}

	/**
	 * 获取验证ID
	 * 
	 * @param servletRequest
	 *            ServletRequest
	 * @return 验证ID
	 */
	protected String getCaptchaId(ServletRequest servletRequest) {
		String captchaId = WebUtils.getCleanParam(servletRequest, captchaIdParam);
		if (captchaId == null) {
			captchaId = ((HttpServletRequest) servletRequest).getSession().getId();
		}
		return captchaId;
	}

	/**
	 * 获取验证码
	 * 
	 * @param servletRequest
	 *            ServletRequest
	 * @return 验证码
	 */
	protected String getCaptcha(ServletRequest servletRequest) {
		return WebUtils.getCleanParam(servletRequest, captchaParam);
	}

	/**
	 * 获取"加密密码"参数名称
	 * 
	 * @return "加密密码"参数名称
	 */
	public String getEnPasswordParam() {
		return enPasswordParam;
	}

	/**
	 * 设置"加密密码"参数名称
	 * 
	 * @param enPasswordParam
	 *            "加密密码"参数名称
	 */
	public void setEnPasswordParam(String enPasswordParam) {
		this.enPasswordParam = enPasswordParam;
	}

	/**
	 * 获取"验证ID"参数名称
	 * 
	 * @return "验证ID"参数名称
	 */
	public String getCaptchaIdParam() {
		return captchaIdParam;
	}

	/**
	 * 设置"验证ID"参数名称
	 * 
	 * @param captchaIdParam
	 *            "验证ID"参数名称
	 */
	public void setCaptchaIdParam(String captchaIdParam) {
		this.captchaIdParam = captchaIdParam;
	}

	/**
	 * 获取"验证码"参数名称
	 * 
	 * @return "验证码"参数名称
	 */
	public String getCaptchaParam() {
		return captchaParam;
	}

	/**
	 * 设置"验证码"参数名称
	 * 
	 * @param captchaParam
	 *            "验证码"参数名称
	 */
	public void setCaptchaParam(String captchaParam) {
		this.captchaParam = captchaParam;
	}

}